<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>抽奖转盘-canvas</title>
  <style>
    *,
    *::before,
    *::after {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    html {
      height: 100%;
      background: #264563;
    }

    .luckdraw {
      width: 300px;
      height: 300px;
      position: relative;
      margin: 100px auto;
      overflow: hidden;
    }

    .luckpanel {
      position: relative;
      width: 100%;
      height: 100%;
      transition: transform 3s ease-in-out;
    }

    #canvas {
      width: 100%;
      height: 100%;
    }

    .prize {
      position: absolute;
      top: 0;
      left: 25%;
      width: 50%;
      height: 50%;
    }

    .prize-item {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      transform-origin: center bottom;
    }

    .prize-item__name {
      position: absolute;
      top: 20px;
      left: 10px;
      width: calc(100% - 20px);
      font-size: 12px;
      text-align: center;
      color: #ff572f;
    }

    .prize-item__img {
      position: absolute;
      top: 50px;
      left: calc(50% - 30px /2);
      width: 30px;
      height: 30px;

    }

    .prize-item__img img {
      width: 100%;
      height: 100%;
      vertical-align: bottom;
    }

    /* 抽奖 */
    .pointer {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      width: 90px;
      height: 90px;
      background-color: orange;
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 30px;
      font-weight: bold;
      cursor: pointer;
      user-select: none;
    }

    .pointer::after {
      content: '';
      position: absolute;
      top: -70px;
      border: 40px solid orange;
      border-left-width: 10px;
      border-right-width: 10px;
      border-left-color: transparent;
      border-top-color: transparent;
      border-right-color: transparent;
    }
  </style>
</head>

<body>
  <div class="luckdraw">
    <div class="luckpanel">
      <canvas id="canvas"></canvas>
      <div class="prize">
        <!-- <div class="prize-item">
          <div class="prize-item__name">奖品</div>
          <div class="prize-item__img">
            <img
              src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d"
              alt="">
          </div>
        </div> -->
      </div>
    </div>
    <div class="pointer"></div>
  </div>
  <script>
    const consts = {
      // 奖品清单
      prizeList: [
        {
          prizeName: '奖品1',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '奖品2',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '奖品3',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '奖品4',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '奖品5',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '奖品6',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
        {
          prizeName: '谢谢参与',
          prizeImg: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.it168.com%2Fn%2F640x480%2F6%2F6414%2F6414197.jpg&refer=http%3A%2F%2Fimage.it168.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1640667263&t=18bb80a694a5ff966713191b4a81745d',
          count: 1,
        },
      ],
      // 每一块扇形的背景色
      prizeBgColors: [
        'rgb(255,231,149)',
        'rgb(255,247,223)',
        'rgb(255,231,149)',
        'rgb(255,247,223)',
        'rgb(255,231,149)',
        'rgb(255,247,223)',
      ],
      // 每一块扇形的外边框颜色
      borderColor: '#ff9800'
    }

    const prizeNum = consts.prizeList.length
    const itemAngle = 360 / prizeNum      // 每个扇形区域的角度
    const offsetAngle = itemAngle / 2
    let isRotating = false
    const circleCount = 3 // 旋转圈数
    const rotateDuration = 3 // 持续时间
    const panel = document.querySelector('.luckpanel')

    // 画出扇形骨架
    function drawPanel() {
      const canvas = document.querySelector('#canvas')
      const ctx = canvas.getContext('2d')
      const w = canvas.clientWidth
      const h = canvas.clientHeight
      const dpr = window.devicePixelRatio
      // 处理设备分辨率
      canvas.width = w * dpr
      canvas.height = h * dpr
      ctx.scale(dpr, dpr)
      // 将画布x轴和y轴各移动一半   再逆时针旋转90°
      ctx.translate(w / 2, h / 2)
      ctx.rotate(-90 * Math.PI / 180)
      // 每块扇形的弧度
      const perRadian = (Math.PI * 2) / prizeNum
      for (let i = 0; i < prizeNum; i++) {
        const radian = perRadian * i
        ctx.strokeStyle = consts.borderColor
        ctx.fillStyle = consts.prizeBgColors[i]
        ctx.beginPath()
        ctx.moveTo(0, 0)
        ctx.arc(0, 0, w / 2, radian, radian + perRadian, false)
        ctx.closePath()
        ctx.stroke()
        ctx.fill()
      }
    }
    function getPrizeItem({ name, src }) {
      const el = document.createElement('div')
      const tpl = `
        <div class="prize-item">
          <div class="prize-item__name">${name}</div>
          <div class="prize-item__img">
            <img
              src="${src}"
              alt="">
          </div>
        </div>
      `
      el.innerHTML = tpl
      return el.firstElementChild
    }
    // 填充奖品内容
    function fillPrize() {
      const container = document.querySelector('.prize')
      consts.prizeList.forEach((item, i) => {
        const el = getPrizeItem({
          name: item.prizeName,
          src: item.prizeImg
        })
        // 旋转
        const currentAngle = itemAngle * i + offsetAngle
        el.style.transform = `rotate(${currentAngle}deg)`
        container.appendChild(el)
      })
    }
    // 获得哪一个奖品
    function getPrizeIndex(prizeList) {
      // 中奖的是哪一个奖品
      let index = 0
      // 当前奖品总数量
      let prizeTotalNum = 0
      for (let i = 0; i < prizeList.length; i++) {
        prizeTotalNum += prizeList[i].count
      }
      if (prizeTotalNum === 0) {
        alert('奖品已抽完');
        return index = -1
      }
      // 产生一个随机数 0-总数
      let random = parseInt(Math.random() * prizeTotalNum)
      // 当前奖品的概率区间
      let currentWeight = 0
      for (let i = 0; i < prizeList.length; i++) {
        currentWeight += prizeList[i].count
        if (random < currentWeight) {
          index = i
          prizeList[i].count--
          break
        }
      }
      return index
    }
    // 抽奖事件
    function bindEvent() {
      document.querySelector('.pointer').addEventListener('click', function () {
        if (isRotating) {
          return
        } else {
          isRotating = true
        }
        const index = getPrizeIndex(consts.prizeList)
        console.log('index', index);
        if (index === -1) {
          isRotating = false
        } else {
          console.log('奖品名称', consts.prizeList[index].prizeName);
          rotate(index)
        }
      })
    }
    // 旋转转盘
    let statrtRotatAngle = 0
    function rotate(index) {
      // statrtRotatAngle % 360 上一次旋转到index的度数
      // statrtRotatAngle - statrtRotatAngle % 360  得到一个度数为 360*n
      // 旋转到索引index商品的度数
      let angleToIndex = 360 - (offsetAngle + itemAngle * index)
      const rotateAngle = statrtRotatAngle - statrtRotatAngle % 360 + circleCount * 360 + angleToIndex
      statrtRotatAngle = rotateAngle
      panel.style.transform = `rotate(${rotateAngle}deg)`
      panel.style.transitionDuration = `${rotateDuration}s`
      setTimeout(() => {
        isRotating = false
      }, 1000)
    }

    function init() {
      // 画出扇形骨架
      drawPanel()
      // 填充奖品内容
      fillPrize()
      // 抽奖事件
      bindEvent()
    }
    document.addEventListener('DOMContentLoaded', init)
  </script>
</body>

</html>